package ru.alt22.visor.php.ui.wizards;
//TODO Interface to set class name, supertype name, interfaces and options.

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.php.internal.core.phpModel.phpElementData.PHPClassData;
import org.eclipse.php.internal.core.phpModel.phpElementData.PHPFunctionData;
import org.eclipse.php.internal.core.phpModel.phpElementData.PHPModifier;
import org.eclipse.php.internal.core.phpModel.phpElementData.PHPFunctionData.PHPFunctionParameter;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;

import ru.alt22.visor.php.core.codegen.PHPClass;
import ru.alt22.visor.php.core.codegen.PHPFile;
import ru.alt22.visor.php.core.codegen.PHPMethodCollection;
import ru.alt22.visor.php.core.codegen.PHPTypeBuilder;
import ru.alt22.visor.php.ui.core.wizards.NewTypeWizard;
import ru.alt22.visor.php.ui.core.wizards.TypePageOptions;

public class NewClassWizard extends NewTypeWizard {
	private NewClassWizardPage page;

	public void addPages() {
		page = new NewClassWizardPage(selection);
		addPage(page);
	}

	public boolean performFinish() {
		final TypePageOptions options = new TypePageOptions(page);
		IRunnableWithProgress op = new IRunnableWithProgress() {
			public void run(IProgressMonitor monitor) throws InvocationTargetException {
				try {
					doFinish(options, monitor);
				} catch (CoreException e) {
					throw new InvocationTargetException(e);
				} finally {
					monitor.done();
				}
			}
		};
		try {
			getContainer().run(true, false, op);
		} catch (InterruptedException e) {
			return false;
		} catch (InvocationTargetException e) {
			Throwable realException = e.getTargetException();
			MessageDialog.openError(getShell(), "Error", realException.getMessage());
			return false;
		}
		return true;
	}

	private void doFinish(TypePageOptions options, IProgressMonitor monitor) throws CoreException {
		monitor.beginTask("Creating " + options.getTypeName() + "...", 2);
		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
		IResource resource = root.findMember(new Path(options.getFolderName()));
		IProject project = resource.getProject();
		if (!resource.exists() || !(resource instanceof IContainer)) {
			throwCoreException("Container \"" + options.getFolderName() + "\" does not exist.");
		}
		IContainer container = (IContainer) resource;
		final IFile file = container.getFile(new Path(options.getTypeName() + PHPTypeBuilder.CLASS_EXTENSION));
		try {
			InputStream stream = openContentStream(options, project);
			if (file.exists()) {
				file.setContents(stream, true, true, monitor);
			} else {
				file.create(stream, true, monitor);
			}
			stream.close();
		} catch (IOException e) {
		}
		monitor.worked(1);
		monitor.setTaskName("Opening file for editing...");
		getShell().getDisplay().asyncExec(new Runnable() {
			public void run() {
				IWorkbenchPage page =
					PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
				try {
					IDE.openEditor(page, file, true);
				} catch (PartInitException e) {
				}
			}
		});
		monitor.worked(1);
	}

	private InputStream openContentStream(TypePageOptions options, IProject project) {
		PHPFile file = new PHPFile();
		PHPClass phpClass = new PHPClass(0, options.getTypePrefix() + options.getTypeName(), options.getSupertypeName());
		file.addEntry(phpClass);

		int classModifier = 0;
		if (options.isConstructor()) {
			int constructorModifier = 0;
			if (options.isConstructorAbstract()) {
				classModifier += PHPModifier.ABSTRACT;
				constructorModifier |= PHPModifier.ABSTRACT; 
			}
			if (options.isConstructorFinal()) {
				constructorModifier |= PHPModifier.FINAL; 
			}
			if (options.isConstructorPrivate()) {
				constructorModifier |= PHPModifier.PRIVATE; 
			}
			if (options.isConstructorProtected()) {
				constructorModifier |= PHPModifier.PROTECTED; 
			}
			if (options.isConstructorPublic()) {
				constructorModifier |= PHPModifier.PUBLIC; 
			}
			StringBuffer body = new StringBuffer();
			if (canUseSupertypeConstructor(options, project)) {
				PHPClassData parentClass = findClassByName(project, options.getSupertypeName());
				while (parentClass.getConstructor() == null) {
					parentClass = findClassByName(project, parentClass.getSuperClassData().getName());
				}
				PHPFunctionData constructor = parentClass.getConstructor();
				PHPFunctionParameter[] parameters = constructor.getParameters();
				body.append("parent::" + PHPTypeBuilder.CLASS_CONSTRUCTOR + "(");
				for (int i = 0; i < parameters.length; i++) {
					body.append("$");
					body.append(parameters[i].getName());
					if (i != parameters.length - 1) {
						body.append(", ");
					}
				}
				body.append(");");
			}
			phpClass.addMethod(constructorModifier | PHPModifier.PUBLIC, PHPTypeBuilder.CLASS_CONSTRUCTOR, body.toString());
		}

		if (options.isDestructor()) {
			phpClass.addMethod(PHPModifier.PUBLIC, PHPTypeBuilder.CLASS_DESCRTUCTOR);
		}
		phpClass.setModifier(classModifier);

		if (options.isInherited()) {
			PHPClassData parentClass = null;
			PHPClassData[] inheritFrom = new PHPClassData[options.getImplements().length];
			if (isValidSupertype(options, project)) {
				parentClass = findClassByName(project, options.getSupertypeName());
			}
			for (int i = 0; i < options.getImplements().length; i++) {
				PHPClassData implementedInterface = options.getImplements()[i];
				phpClass.addInterface(implementedInterface.getName());
				inheritFrom[i] = implementedInterface;
			}

			PHPMethodCollection methods = PHPTypeBuilder.findUnimplementedMethods(parentClass, inheritFrom);
			for (int i = 0; i < methods.length(); i++) {
				phpClass.addMethod(methods.get(i));
			}
		}

		return new ByteArrayInputStream(file.toString().getBytes());
	}

	private boolean canUseSupertypeConstructor(TypePageOptions options, IProject project) {
		if (!options.isSuperclassConstructor()) {
			return false;
		}
		return isValidSupertype(options, project);
	}

	private boolean isValidSupertype(TypePageOptions options, IProject project) {
		if (options.getSupertypeName() == null) {
			return false;
		}
		if (options.getSupertypeName().length() == 0) {
			return false;
		}
		if (!PHPTypeBuilder.isClassExists(project, options.getSupertypeName())) {
			return false;
		}
		return true;
	}

}